"""
PPT builder for converting structured content to PowerPoint presentations.
"""

from pathlib import Path
from typing import List, Optional

from pptx import Presentation
from pptx.slide import Slide
from pptx.enum.shapes import MSO_SHAPE_TYPE

from ..parser.content_types import (
    AnimatedContent,
    BulletListContent,
    CodeBlockContent,
    ContentNode,
    ContentType,
    DocumentStructure,
    ImageContent,
    VideoContent,
    Section,
    TextContent,
)
from ..styling.theme_manager import ThemeManager
from ..media.image_processor import ImageProcessor
from ..media.video_processor import VideoProcessor


class PPTBuilder:
    """Builds PowerPoint presentations from structured Markdown content."""
    
    def __init__(self, theme_name: str = "default"):
        self.theme_manager = ThemeManager(theme_name)
        self.image_processor = ImageProcessor(self.theme_manager)
        self.video_processor = VideoProcessor(self.theme_manager)
        self.presentation = None
    
    def build_from_structure(self, doc_structure: DocumentStructure) -> Presentation:
        """Build a PowerPoint presentation from document structure."""
        self.presentation = Presentation()
        
        # Apply theme to presentation
        self.theme_manager.apply_theme(self.presentation)
        
        # Remove default slide if it exists
        if len(self.presentation.slides) > 0:
            self._remove_slide(self.presentation.slides[0])
        
        # Process sections
        for section in doc_structure.sections:
            if section.is_title_slide:
                self._create_title_slide(section)
            elif section.is_content_slide or section.level == 3:
                # Process level 2 and level 3 as content slides
                self._create_content_slide(section)
        
        # If no slides were created, create a title slide
        if len(self.presentation.slides) == 0:
            self._create_default_title_slide(doc_structure.title)
        
        return self.presentation
    
    def save(self, output_path: str) -> None:
        """Save the presentation to a file."""
        if self.presentation is None:
            raise ValueError("No presentation to save. Call build_from_structure first.")
        
        self.presentation.save(output_path)
    
    def _remove_slide(self, slide: Slide) -> None:
        """Remove a slide from the presentation."""
        slide_id = slide.slide_id
        self.presentation.part.drop_rel(slide.part.partname)
        del self.presentation.slides._sldIdLst[slide_id]
    
    def _create_title_slide(self, section: Section) -> Slide:
        """Create a title slide from a section."""
        # Use blank slide layout
        slide_layout = self.presentation.slide_layouts[6]  # Blank layout
        slide = self.presentation.slides.add_slide(slide_layout)
        
        # Get layout dimensions
        layout = self.theme_manager.get_layout_dimensions()
        
        # Add title
        title_shape = slide.shapes.add_textbox(
            layout['title_left'],
            layout['title_top'],
            layout['title_width'],
            layout['title_height']
        )
        title_shape.text = section.heading
        self.theme_manager.style_title_slide(slide, title_shape)
        
        # Add content if any
        if section.content:
            content_text = self._extract_text_from_content(section.content)
            if content_text:
                subtitle_shape = slide.shapes.add_textbox(
                    layout['content_left'],
                    layout['content_top'],
                    layout['content_width'],
                    layout['content_height'] // 2
                )
                subtitle_shape.text = content_text
                self.theme_manager.style_title_slide(slide, None, subtitle_shape)
        
        return slide
    
    def _create_content_slide(self, section: Section) -> Slide:
        """Create a content slide from a section."""
        # Use blank slide layout
        slide_layout = self.presentation.slide_layouts[6]  # Blank layout
        slide = self.presentation.slides.add_slide(slide_layout)
        
        # Get layout dimensions
        layout = self.theme_manager.get_layout_dimensions()
        
        # Add slide title
        title_shape = slide.shapes.add_textbox(
            layout['slide_title_left'],
            layout['slide_title_top'],
            layout['slide_title_width'],
            layout['slide_title_height']
        )
        title_shape.text = section.heading
        self.theme_manager.style_content_slide_title(title_shape)
        
        # Add content
        current_y = layout['content_top']
        content_height = layout['content_height']
        
        for content_node in section.content:
            current_y = self._add_content_to_slide(slide, content_node, current_y, layout)
            
            # Check if we're running out of space
            if current_y >= layout['content_top'] + content_height:
                break
        
        return slide
    
    def _add_content_to_slide(self, slide: Slide, content_node: ContentNode, 
                            current_y: float, layout: dict) -> float:
        """Add a content node to the slide and return the new Y position."""
        
        if content_node.type == ContentType.TEXT:
            return self._add_text_content(slide, content_node.data, current_y, layout)
        
        elif content_node.type == ContentType.BULLET_LIST:
            return self._add_bullet_list(slide, content_node.data, current_y, layout)
        
        elif content_node.type == ContentType.IMAGE:
            return self._add_image_content(slide, content_node.data, current_y, layout)
        
        elif content_node.type == ContentType.VIDEO:
            return self._add_video_content(slide, content_node.data, current_y, layout)
        
        elif content_node.type == ContentType.CODE_BLOCK:
            return self._add_code_block(slide, content_node.data, current_y, layout)
        
        elif content_node.type == ContentType.ANIMATED_CONTENT:
            return self._add_animated_content(slide, content_node.data, current_y, layout)
        
        return current_y
    
    def _add_text_content(self, slide: Slide, text_content: TextContent, 
                         current_y: float, layout: dict) -> float:
        """Add text content to slide."""
        from pptx.util import Inches
        
        # Estimate height needed (rough calculation)
        lines = text_content.text.count('\n') + 1
        height_needed = Inches(0.3 * lines)  # Approximately 0.3 inches per line
        
        text_shape = slide.shapes.add_textbox(
            layout['content_left'],
            current_y,
            layout['content_width'],
            height_needed
        )
        text_shape.text = text_content.text
        self.theme_manager.style_body_text(text_shape)
        
        return current_y + height_needed + Inches(0.2)  # Add some spacing
    
    def _add_bullet_list(self, slide: Slide, bullet_content: BulletListContent, 
                        current_y: float, layout: dict) -> float:
        """Add bullet list to slide."""
        from pptx.util import Inches
        
        # Estimate height needed
        height_needed = Inches(0.3 * len(bullet_content.items))
        
        text_shape = slide.shapes.add_textbox(
            layout['content_left'],
            current_y,
            layout['content_width'],
            height_needed
        )
        
        # Create bullet list text
        bullet_text = '\n'.join(f"• {item}" for item in bullet_content.items)
        text_shape.text = bullet_text
        self.theme_manager.style_bullet_list(text_shape)
        
        return current_y + height_needed + Inches(0.2)
    
    def _add_image_content(self, slide: Slide, image_content: ImageContent, 
                          current_y: float, layout: dict) -> float:
        """Add image to slide."""
        try:
            image_height = self.image_processor.add_image_to_slide(
                slide, image_content.path, current_y
            )
            return current_y + image_height + self.theme_manager.styles.CONTENT_MARGIN_BOTTOM
        except Exception as e:
            # If image fails to load, add error text
            from pptx.util import Inches
            text_shape = slide.shapes.add_textbox(
                layout['content_left'],
                current_y,
                layout['content_width'],
                Inches(0.5)
            )
            text_shape.text = f"[Image not found: {image_content.path}]"
            self.theme_manager.style_body_text(text_shape)
            return current_y + Inches(0.7)
    
    def _add_video_content(self, slide: Slide, video_content: VideoContent, 
                          current_y: float, layout: dict) -> float:
        """Add video to slide."""
        try:
            video_height = self.video_processor.add_video_to_slide(
                slide, 
                video_content.path, 
                current_y,
                poster_frame_path=video_content.poster_frame,
                alt_text=video_content.alt_text
            )
            return current_y + video_height + self.theme_manager.styles.CONTENT_MARGIN_BOTTOM
        except Exception as e:
            # If video fails to load, add error text
            from pptx.util import Inches
            text_shape = slide.shapes.add_textbox(
                layout['content_left'],
                current_y,
                layout['content_width'],
                Inches(0.5)
            )
            text_shape.text = f"[Video not found: {video_content.path}]"
            self.theme_manager.style_body_text(text_shape)
            return current_y + Inches(0.7)
    
    def _add_code_block(self, slide: Slide, code_content: CodeBlockContent, 
                       current_y: float, layout: dict) -> float:
        """Add code block to slide."""
        from pptx.util import Inches
        
        # Estimate height needed
        lines = code_content.code.count('\n') + 1
        height_needed = Inches(0.25 * lines)  # Slightly smaller font for code
        
        text_shape = slide.shapes.add_textbox(
            layout['content_left'],
            current_y,
            layout['content_width'],
            height_needed
        )
        
        # Add language label if available
        display_text = code_content.code
        if code_content.language:
            display_text = f"[{code_content.language}]\n{display_text}"
        
        text_shape.text = display_text
        
        # Style as monospace (using a different styling approach)
        text_frame = text_shape.text_frame
        for paragraph in text_frame.paragraphs:
            for run in paragraph.runs:
                font = run.font
                font.name = "Consolas"  # Monospace font
                font.size = self.theme_manager.styles.BODY_FONT_SIZE
        
        return current_y + height_needed + Inches(0.2)
    
    def _add_animated_content(self, slide: Slide, animated_content: AnimatedContent, 
                            current_y: float, layout: dict) -> float:
        """Add animated content to slide."""
        # For PowerPoint format, we render the content normally but add a comment 
        # indicating the animation information. Full animation support is available 
        # in HTML and video exports.
        
        # Process the wrapped content normally
        new_y = self._add_content_to_slide(slide, animated_content.content, current_y, layout)
        
        # Add animation comment to the slide (visible in PowerPoint notes)
        if hasattr(slide, 'notes_slide'):
            try:
                notes_slide = slide.notes_slide
                if notes_slide.notes_text_frame.text:
                    notes_slide.notes_text_frame.text += f"\n"
                else:
                    notes_slide.notes_text_frame.text = ""
                    
                # Add animation details to notes
                animation_info = (f"Animation: {animated_content.animation_type}, "
                                f"Duration: {animated_content.duration}s, "
                                f"Delay: {animated_content.delay}s")
                if animated_content.direction:
                    animation_info += f", Direction: {animated_content.direction}"
                
                notes_slide.notes_text_frame.text += f"[{animation_info}]"
            except Exception:
                # If notes fail, continue without animation metadata
                pass
        
        return new_y
    
    def _create_default_title_slide(self, title: str) -> Slide:
        """Create a default title slide if no content is provided."""
        slide_layout = self.presentation.slide_layouts[6]  # Blank layout
        slide = self.presentation.slides.add_slide(slide_layout)
        
        layout = self.theme_manager.get_layout_dimensions()
        
        title_shape = slide.shapes.add_textbox(
            layout['title_left'],
            layout['title_top'],
            layout['title_width'],
            layout['title_height']
        )
        title_shape.text = title
        self.theme_manager.style_title_slide(slide, title_shape)
        
        return slide
    
    def _extract_text_from_content(self, content_nodes: List[ContentNode]) -> str:
        """Extract plain text from content nodes for subtitle."""
        text_parts = []
        for node in content_nodes:
            if node.type == ContentType.TEXT:
                text_parts.append(node.data.text)
            elif node.type == ContentType.BULLET_LIST:
                text_parts.extend(node.data.items)
        
        return '\n'.join(text_parts)[:200]  # Limit length for subtitle